#!/usr/bin/env python
# $Id: whois.py ddcad87fab9c 2013/08/17 19:20:30 pgurumur $
# Copyright (c) 2012 Prabhu Gurumurthy <pgurumur@gmail.com>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#

from __future__ import division

__version__  = "$Revision: ddcad87fab9c $"
__author__   = "$Author: pgurumur $"
__modified__ = "$Date: 2013-08-17 19:20:30Z $"

import optparse
import os
import socket
import sys

import dns.resolver
from venya.lib import exception, cmdparser, ip, stdlib

dnsquery = lambda query: dns.resolver.query(query, "TXT")
resolve = lambda record: str(dnsquery(record).rrset).split("TXT")[1].replace(
      "\"", "").strip()
getasn = lambda asnumber: resolve("AS%d.asn.cymru.com" %asnumber).split(
      "|")[-1].strip()

class whois(cmdparser.GetOpt):
   option_list = (
         optparse.make_option("-4", "--ipv4", action = "store_true",
            help = "Use IPv4 to query whois", default = False, dest = "v4"),
         optparse.make_option("-6", "--ipv6", action = "store_true",
            help = "Use IPv6 to query whois", default = False, dest = "v6"),
         optparse.make_option("-f", "--filename", type = "str",
            help = "Filename with list of IP", dest = "filename"),
   ) + cmdparser.GetOpt.option_list

   help = """
   whois script using cymru DNS to gather data about IPv4 or IPv6 addresses
   The following input values can be used
   1. DNS entry (resolvable to valid IPv4/IPv6 address)
   2. IPv4 address
   3. IPv4 subnet
   4. IPv6 address
   5. IPv4 subnet

   This script can take more than one entry in sequence
   Valid example:
      whois.py www.facebook.com www.google.com 17.0.0.0/9

   Version can be forced, example to obtain IPv6 information for google
      whois.py -6 www.google.com

   Default version is IPv4

   Examples:
      whois.py -4 www.stumbleupon.com
      whois.py -6 www.facebook.com
      whois.py www.google.com 4.2.2.2 www.facebook.com
   """

   usage = "[ options ] host(s)"

   def __init__(self, **Keywords):
      """
      @brief Init function that usually declares variables for this class and
             then calls super, calling parent is important
      @param self 'this' pointer for the class
      @param Keyword keywords parameter
      @return Usually returns the class object
      """
      super(self.__class__, self).__init__(**Keywords)
      self._handle = None
      self._ip = None
      self._asinfo = {}
      self._destas = {}

   def _getv4peer(self, address):
      if address:
         query = "%s.peer.asn.cymru.com" %(stdlib.ip2ptr(address))
         data = resolve(query)
         asnumber = data.split("|")[0].strip()
         print("Peering entities: ")
         try:
            for asn in asnumber.split(" "):
               asn = int(asn)
               asninfo = getasn(asn)
               """
               if asn in self._asinfo:
                  value = self._asinfo[asn][1] + 1
                  self._asinfo[asn] = (self._asinfo[asn][0], value)
               else:
                  self._asinfo[asn] = (asninfo, 1)
               """

               self.cout("AS%s - %s\n", asn, asninfo)
         except dns.resolver.NXDOMAIN:
            pass

         print("-------------------------------------")

   def _tryip(self, address):
      temp = None
      try:
         temp = ip.ip(address)
      except exception.IPError as err:
         pass
      else:
         temp = temp[0]

      return temp

   def _getip(self, address, version):
      temp = None
      try:
         temp = socket.getaddrinfo(address, 0, version)[0][-1][0]
      except socket.gaierror as (errno, message):
         stdlib.colorprint("red", "%s: %s: %s", str(self), message, address)
      except socket.error as (errno, message):
         temp = self._tryip(address)
         if not temp:
            stdlib.colorprint("red", "%s: %s: (%d): %s", str(self), message,
                  errno, address)
      if temp:
         self.cout("%s details\n", address)
         if version == socket.AF_INET:
            query = "%s.origin.asn.cymru.com" %stdlib.ip2ptr(temp)
         elif version == socket.AF_INET6:
            query = "%s.origin6.asn.cymru.com" %stdlib.ip2ptr6(temp)

         data = resolve(query).split("|")
         self._ip = ip.ip(data[1].strip())
         tmp = data[0].strip().split(" ")
         for ix in tmp:
            try:
               asn = int(ix)
               asinfo = getasn(asn)
               self.cout("Company: %s\n", asinfo)
               self.cout("IP: %s\n", self._ip)
               self.cout("RIR: %s\n", data[3].strip())
               if asn in self._asinfo:
                  value = self._asinfo[asn][1] + 1
                  self._asinfo[asn] = (self._asinfo[asn][0], value)
               else:
                  self._asinfo[asn] = (asinfo, 1)

               self.cout("ASNumber: %s\n", ix)
            except dns.resolver.NXDOMAIN:
               pass

         if version == socket.AF_INET:
            self._getv4peer(str(self._ip[1]))

   @cmdparser.getopts
   def handle(self):
      host = []
      if self.options.filename and os.path.exists(self.options.filename):
         with open(self.options.filename, "r") as fh:
            for line in fh.readlines():
               host.append(line.strip())
      else:
         try:
            host = self.arguments[1:]
         except IndexError:
            self.error("Need more arguments!")

      if self.options.v4 is False and self.options.v6 is False:
         version = socket.AF_INET
      else:
         if self.options.v4:
            version = socket.AF_INET

         if self.options.v6:
            version = socket.AF_INET6

      for ix in host:
         self._getip(ix, version)

      for asn in sorted(self._asinfo):
         value = self._asinfo[asn]
         percent = (value[1] / aslen) * 100
         self.cout("ASN %d, Entity: %s, counter: %d (%0.02f)\n", asn,
               value[0], value[1], percent)

      """
      print("------------------------")
      for asn in sorted(self._destas):
         value = self._destas[asn]
         self.cout("ASN %s, Entity: %s, counter: %d\n", asn, value[0], value[1])
      """

if __name__ == "__main__":
   client = None
   try:
      client = whois(version = 1.0, args = sys.argv)
      if len(client) <= 1:
         client.printhelp()
      else:
         client.handle()
   except exception.GetOptError as err:
      stdlib.colorprint(err)
      sys.exit(1)
